package bert.collection.extensions;

import java.util.ArrayList;
import java.util.Collection;

public class CX {

	public static <T> T findFirst(Collection<T> collection, Predicate<T> p)
	{
		for (T obj : collection)
		{
			if (p.Matches(obj))
				return obj;
		}
		
		return null;
	}
	
	public static <T> T findSingle(Collection<T> collection, Predicate<T> p)
	{
		boolean found = false;
		T result = null;
		
		for (T obj : collection)
		{
			if (p.Matches(obj))
			{
				if(!found)
				{
					result = obj;
					found = true;
				}
				else
				{
					throw new RuntimeException();
				}
			}
		}
		
		return result;
	}
	
	public static <T> int findFirstIndex(T[] array, Predicate<T> p)
	{
		for (int i = 0; i < array.length; ++i)
		{
			if (p.Matches(array[i]))
				return i;
		}
		
		return -1;
	}
	
	public static <T extends Comparable<T>> int findFirstIndex(T[] array, final T item)
	{
		return findFirstIndex(array, new Predicate<T>() {
			@Override public boolean Matches(T obj) {
				return item.compareTo(obj) == 0;
			}
		});
	}
	
	public static <TSource, TTarget extends Comparable<TTarget>> TTarget Min(
			Collection<TSource> collection, 
			Selector<TSource, TTarget> selector)
	{
		TTarget min = null;
		
		for (TSource obj : collection)
		{
			TTarget current = selector.Select(obj);
			
			if (min == null)
			{
				min = current;
			}
			else if (current.compareTo(min) < 0)
			{
				min = current;
			}
		}
		
		return min;
	}
	
	public static <TSource, TTarget extends Comparable<TTarget>> TTarget Min(
			TSource[] collection, 
			Selector<TSource, TTarget> selector)
	{
		TTarget min = null;
		
		for (TSource obj : collection)
		{
			TTarget current = selector.Select(obj);
			
			if (min == null)
			{
				min = current;
			}
			else if (current.compareTo(min) < 0)
			{
				min = current;
			}
		}
		
		return min;
	}
	
	public static <TSource, TTarget extends Comparable<TTarget>> TTarget Max(
			Collection<TSource> collection, 
			Selector<TSource, TTarget> selector)
	{
		TTarget min = null;
		
		for (TSource obj : collection)
		{
			TTarget current = selector.Select(obj);
			
			if (min == null)
			{
				min = current;
			}
			else if (current.compareTo(min) > 0)
			{
				min = current;
			}
		}
		
		return min;
	}
	
	public static <TSource, TTarget extends Comparable<TTarget>> TTarget Max(
			TSource[] collection, 
			Selector<TSource, TTarget> selector)
	{
		TTarget min = null;
		
		for (TSource obj : collection)
		{
			TTarget current = selector.Select(obj);
			
			if (min == null)
			{
				min = current;
			}
			else if (current.compareTo(min) > 0)
			{
				min = current;
			}
		}
		
		return min;
	}
	
	public static <T> boolean Correspond(T[] col1, T[] col2, EqComparer<T> comparer) {
		
		assert col1 != null;
		assert col2 != null;
		
		if (col1.length != col2.length)
			return false;
		
		for ( int i = 0; i < col1.length; ++i )
		{
			if (!comparer.AreEquivalent(col1[i], col2[i]))
				return false;
		}
		
		return true;
	}
	
	public static <T> T Fold(Collection<T> collection, Func2<T> f) 
	{
		boolean first= true;
		T acc = null;
		
		for (T item : collection)
		{
			if (first) {
				acc = item;
				first = false;
			}
			else
			{
				acc = f.apply(item, acc);
			}
		}
		
		return acc;
	}
	
	public static <T> Collection<T> Filter(Collection<T> collection, Predicate<T> pred)
	{
		ArrayList<T> result = new ArrayList<T>();
		
		for (T item : collection)
		{
			if (pred.Matches(item))
				result.add(item);
		}
		
		return result;
	}
}
